package com.distribute.distributelock.common.lock;

import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.concurrent.*;

/**
 * @description:
 * @author: 刘帅
 * @time: 2020/5/14 9:30
 */
public class MultiThreadAccessByLock {

    private static JedisPool jedis = new JedisPool("127.0.0.1", 6379);

    public static int corePoolSize = Runtime.getRuntime().availableProcessors();

    private static ThreadPoolExecutor executor  = new ThreadPoolExecutor(corePoolSize, corePoolSize+1, 10l, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(1000));
    /**
     *@描述
     *@创建人 liushuai
     *@创建时间 2020/5/14 15:46
        * @param null
     *@返回值
     * redis  set nx
     */
    @Test
    public void testLockCase1() {
        //为了测试
        CountDownLatch countDownLatch = new CountDownLatch(100);
        for (int i = 0; i < 100; i++) {
            final int userId = i;
            Runnable task = () -> {
                Jedis client = jedis.getResource();
                client.auth("123456");
                LockCase1 lockCase1 = new LockCase1(client, "testLockCase1");
                try {
                    lockCase1.lock();
//                    System.out.println("第" + userId + "个人上锁");
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lockCase1.unlock();
                    countDownLatch.countDown();
                    System.out.println("释放锁");
                    // 需要释放连接， 否则 连接池中的连接用完之后造成不可用状态
                    client.close();
                }
            };
            executor.submit(task);
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("结束了");
    }

    /**
     *@描述
     *@创建人 liushuai
     *@创建时间 2020/5/14 15:29
        * @param null
     *@返回值
     * 测试死锁情况， 其他线程等待时间到了释放锁
     * 死锁 睡 4s , redis set nx ex 3s
     *
     * 存在的问题：
     * 1.客户端A 因为业务阻塞了 50s
     * 2.客户端A持有的锁的 时间到了， 自动释放了锁
     * 3.客户端B 获取相同资源的锁
     * 4.客户端A 50 s 的时间执行完毕， A  把 B的锁释放了
     */
    @Test
    public void testLockCase2() {
        CountDownLatch latch = new CountDownLatch(101);

        Runnable runnable = () -> {
            Jedis client = jedis.getResource();
            client.auth("123456");
            LockCase2 lockCase2 = new LockCase2(client, "test:lock:lockKey");
            try {
                lockCase2.lock();
                TimeUnit.SECONDS.sleep(4);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lockCase2.unlock();
                client.close();
                latch.countDown();
            }
        };
        for (int i = 0; i < 100; i++) {
            final int userId = i;
            if (i == 20) {
                System.out.println("死锁测试开始");
                executor.submit(runnable);
            }
            Runnable runnable1 = () -> {
                Jedis client = jedis.getResource();
                client.auth("123456");
                LockCase2 lockCase2 = new LockCase2(client, "test:lock:lockKey");
                try {
                    lockCase2.lock();
                    System.out.println(userId);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lockCase2.unlock();
                    client.close();
                    latch.countDown();
                }
            };
            executor.submit(runnable1);
        }

        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
        }
    }

    /**
     *@描述
     *@创建人 liushuai
     *@创建时间 2020/5/14 15:52
        * @param null
     *@返回值
     * 1. 保证 锁的过期时间大于 业务执行时间
     * 2. 保证锁不会被其他线程给误释放
     */
    @Test
    public void testLockCase3() {
        // 保证锁不会被误删除
        // 这个问题可以通过设置value为当前客户端生成的一个随机字符串，
        // 且保证在足够长的一段时间内在所有客户端的所有获取锁的请求中都是唯一的。

        CountDownLatch latch = new CountDownLatch(101);

        Runnable runnable = () -> {
            Jedis client = jedis.getResource();
            client.auth("123456");
            LockCase2 lockCase2 = new LockCase2(client, "test:lock:lockKey");
            try {
                lockCase2.lock();
                TimeUnit.SECONDS.sleep(4);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lockCase2.unlock();
                client.close();
                latch.countDown();
            }
        };
        for (int i = 0; i < 100; i++) {
            final int userId = i;
            if (i == 20) {
                System.out.println("死锁测试开始");
                executor.submit(runnable);
            }
            Runnable runnable1 = () -> {
                Jedis client = jedis.getResource();
                client.auth("123456");
                LockCase2 lockCase2 = new LockCase2(client, "test:lock:lockKey");
                try {
                    lockCase2.lock();
                    System.out.println(userId);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lockCase2.unlock();
                    client.close();
                    latch.countDown();
                }
            };
            executor.submit(runnable1);
        }

        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
        }
    }

}
